#include <dde_kit/pgtab.h>
#include <dde_kit/assert.h>

#include <eku/stdio.h>
#include <eku/stdlib.h>
#include <eku/mmap.h>

/* This PTR will be initialized as a 4M array to record the inv map of physical memory */
dde_kit_addr_t *inv_map;

void
dde_kit_pgtab_set_region(void *virt, dde_kit_addr_t phys, unsigned pages)
{
	 dde_kit_assert(((uint32_t)virt & (DDE_KIT_PAGE_SIZE - 1)) == 0);
	 dde_kit_assert(((uint32_t)phys & (DDE_KIT_PAGE_SIZE - 1)) == 0);
	 int i;
	 for (i = 0; i != pages; ++ i)
	 {
		  uint32_t cur = (uint32_t)virt + (i << DDE_KIT_PAGE_SHIFT);
		  mmap_set(cur, (phys + (i << DDE_KIT_PAGE_SHIFT)), MMAP_W);
		  inv_map[(phys >> DDE_KIT_PAGE_SHIFT) + i] = cur;
		  if (i == pages - 1)
			   inv_map[(phys >> DDE_KIT_PAGE_SHIFT) + i] |= 1;
	 }
}

void
dde_kit_pgtab_set_region_with_size(void *virt, dde_kit_addr_t phys,
								   dde_kit_size_t size)
{
	 dde_kit_assert(((uint32_t)virt & (DDE_KIT_PAGE_SIZE - 1)) == 0);
	 dde_kit_assert(((uint32_t)phys & (DDE_KIT_PAGE_SIZE - 1)) == 0);
	 unsigned pages = (size + DDE_KIT_PAGE_SHIFT - 1) >> DDE_KIT_PAGE_SHIFT;
	 int i;
	 for (i = 0; i != pages; ++ i)
	 {
		  uint32_t cur = (uint32_t)virt + (i << DDE_KIT_PAGE_SHIFT);
		  mmap_set(cur, phys + (i << DDE_KIT_PAGE_SHIFT), MMAP_W);
		  inv_map[(phys >> DDE_KIT_PAGE_SHIFT) + i] = cur;
		  if (i == pages - 1)
			   inv_map[(phys >> DDE_KIT_PAGE_SHIFT) + i] |= 1;
	 }
}

void
dde_kit_pgtab_clear_region(void *virt)
{
	 dde_kit_assert(((uint32_t)virt & (DDE_KIT_PAGE_SIZE - 1)) == 0);
	 int i = 0;
	 while (i)
	 {
		  uint32_t cur = (uint32_t)virt + (i << DDE_KIT_PAGE_SHIFT);
		  uint32_t paddr = mmap_get(cur, NULL);
		  uint32_t im = inv_map[(paddr >> DDE_KIT_PAGE_SHIFT) + i];
		  inv_map[(paddr >> DDE_KIT_PAGE_SHIFT) + i] = -1;
		  mmap_set(cur, 0, 0);
		  if ((im & 1) == 1) break;
		  ++ i;
	 }
}

dde_kit_addr_t
dde_kit_pgtab_get_physaddr(void *virt)
{
	 return (dde_kit_addr_t)mmap_get((uint32_t)virt, 0) + ((dde_kit_addr_t)virt & (DDE_KIT_PAGE_SIZE - 1));
}

dde_kit_addr_t
dde_kit_pgtab_get_virtaddr(dde_kit_addr_t phys)
{
	 return inv_map[phys >> DDE_KIT_PAGE_SHIFT] & (~0xFFF) + (phys & (DDE_KIT_PAGE_SIZE - 1));
}

dde_kit_size_t
dde_kit_pgtab_get_size(void *virt)
{
	 dde_kit_assert(((uint32_t)virt & (DDE_KIT_PAGE_SIZE - 1)) == 0);
	 int i = 0;
	 while (i)
	 {
		  uint32_t cur = (uint32_t)virt + (i << DDE_KIT_PAGE_SHIFT);
		  physaddr_t paddr = mmap_get(cur, NULL);
		  if ((inv_map[(paddr >> DDE_KIT_PAGE_SHIFT) + i] & 1) == 1)
			   break;
		  ++ i;
	 }
	 return i << DDE_KIT_PAGE_SHIFT;
}

#include <eku/symbol.h>

void
dde_kit_pgtab_symbol(void)
{
	 export_symbol("dde_kit_pgtab_set_region", &dde_kit_pgtab_set_region, 1);
	 export_symbol("dde_kit_pgtab_set_region_with_size", &dde_kit_pgtab_set_region_with_size, 1);
	 export_symbol("dde_kit_pgtab_clear_region", &dde_kit_pgtab_clear_region, 1);
	 export_symbol("dde_kit_pgtab_get_physaddr", &dde_kit_pgtab_get_physaddr, 1);
	 export_symbol("dde_kit_pgtab_get_virtaddr", &dde_kit_pgtab_get_virtaddr, 1);
	 export_symbol("dde_kit_pgtab_get_size", &dde_kit_pgtab_get_size, 1);
}
